package com.xuan.chatroom.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xuan.chatroom.component.OnlineUserManager;
import com.xuan.chatroom.constants.Constant;
import com.xuan.chatroom.mapper.MessageSessionMapper;
import com.xuan.chatroom.model.*;
import com.xuan.chatroom.service.FriendService;
import com.xuan.chatroom.service.MessageService;
import com.xuan.chatroom.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.List;

@Component
@Slf4j
public class WebSocketAPI extends TextWebSocketHandler {
    @Autowired
    private OnlineUserManager onlineUserManager;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private MessageSessionMapper messageSessionMapper;
    @Autowired
    private MessageService messageService;
    @Autowired
    private FriendService friendService;


    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("[WebSocketAPI] 连接成功!");
        // session.getAttributes() 得到一个 Map,里面的元素为服务器存储的Attribute
        String token = (String) session.getAttributes().get(Constant.WEBSOCKET_USER_TOKEN);
        log.info("[WebSocketAPI] afterConnectionEstablished, token: " + token);

        Claims claims = JwtUtil.parseToken(token);
        if (claims == null) {
            return;
        }


        // 根据 userId 往hash表中存储对应客户端的WebSocketSession对象
        onlineUserManager.online((Integer) claims.get(Constant.CLAIM_USERID), session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1. 获取用户信息,检查其登录状态
        String token = (String) session.getAttributes().get(Constant.WEBSOCKET_USER_TOKEN);
        Claims claims = JwtUtil.parseToken(token);
        // 用户携带令牌可能出现过期的情况,因此不能后续的实时通信操作
        if(claims == null) {
            System.out.println("[WebSocketAPI.handleTextMessage] 用户token过期，无法进行消息转发！");
            session.close(new CloseStatus(4888));
            return;
        }


        User user = new User((Integer) claims.get(Constant.CLAIM_USERID), (String) claims.get(Constant.CLAIM_USERNAME), "");
        // 2. 将获取到的 json格式的数据 转换为Java对象
        MessageRequest req = (MessageRequest) objectMapper.readValue(message.getPayload(), MessageRequest.class);
        switch (req.getType()) {
            case "message":
                transferMessage(user, req);
                break;
            case "addFriend": {
                AddRequest req2 = objectMapper.readValue(message.getPayload(), AddRequest.class);
                transferAddRequest(user, req2);
                break;
            }
            case "addResponse": {
                AddRequest req2 = objectMapper.readValue(message.getPayload(), AddRequest.class);
                transferAddResponse(req2);
                break;
            }
        }

    }

    /**
     * 转发聊天消息
     * @param user
     * @param req
     * @throws IOException
     */
    private void transferMessage(User user, MessageRequest req) throws IOException {
        log.info(req.toString());
        // 1. 构建一个要转发的 Message 对象
        MessageResponse resp = new MessageResponse();
        resp.setType("message");
        resp.setFromId(user.getId());
        resp.setFromName(user.getUsername());
        resp.setSessionId(req.getSessionId());
        resp.setContent(req.getContent());
        // 将java对象转换为 Json字符串
        String responseJson = objectMapper.writeValueAsString(resp);

        // 2. 查询消息会话中有存在哪些用户(除了自己)
        List<Friend> friends = messageSessionMapper.queryFriendsBySessionId(req.getSessionId(), user.getId());
        friends.add(new Friend(user.getId(), user.getUsername()));

        // 3. 遍历 friends,查看会话中的用户是否在线,如果在线则进行消息的发送. 注意向自己也发送一份消息!
        for (Friend friend : friends) {
            WebSocketSession webSocketSession = onlineUserManager.getSession(friend.getFriendId());
            if (webSocketSession == null) {
                continue;
            }
            webSocketSession.sendMessage(new TextMessage(responseJson));
        }

        // 4. 往数据库插入该消息记录
        Message message = new Message(null, req.getSessionId(), user.getId(), user.getUsername(), req.getContent(), null);
        messageService.insertMessage(message);
    }

    /**
     * 转发好友申请
     * @param user
     * @param req
     * @throws IOException
     */
    private void transferAddRequest(User user, AddRequest req) throws IOException {
        System.out.println(req);
        // 1. 构造一个要转发的 AddRequest 对象
        req.setFromName(user.getUsername());
        String resp = objectMapper.writeValueAsString(req);

        // 2. 向 toId 用户发送好友添加申请
        WebSocketSession webSocketSession = onlineUserManager.getSession(req.getToId());
        if (webSocketSession != null) {

            webSocketSession.sendMessage(new TextMessage(resp));
        }

        // 3. 往数据库插入好友申请记录
        Integer res = friendService.insertAddRequest(req);
    }


    /**
     * 转发好友申请处理结果
     * @param req
     * @throws IOException
     */
    public void transferAddResponse(AddRequest req) throws IOException {
        // 1. 构造要转发的数据，此处直接将发送来的数据原地返回即可
        String resp = objectMapper.writeValueAsString(req);

        // 2. 直接将发送来的请求转发给 添加者和被添加者(在线)
        WebSocketSession loginSession = onlineUserManager.getSession(req.getFromId());
        if (loginSession != null) {
            loginSession.sendMessage(new TextMessage(resp));
        }
        WebSocketSession toSession = onlineUserManager.getSession(req.getToId());
        if (toSession != null) {
            toSession.sendMessage(new TextMessage(resp));
        }

        // 3. 往数据库处理好友申请记录 (互相添加好友或只修改validFlag)
        friendService.handle(req);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("[WebSocketAPI] 连接异常! " + exception.getMessage());

        onlineUserManager.offline((Integer) session.getAttributes().get(Constant.WEBSOCKET_USER_ID), session);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("[WebSocketAPI] 关闭! " + status.toString());

        onlineUserManager.offline((Integer) session.getAttributes().get(Constant.WEBSOCKET_USER_ID), session);
    }
}
